package org.deltaset.meta.internal;

import java.lang.reflect.Method;
import java.util.List;
import org.deltaset.meta.Finder;
import org.deltaset.meta.MetaAction;
import org.deltaset.meta.annotation.DsFirstResult;
import org.deltaset.meta.annotation.DsMaxResult;

/**
 * 
//CollectionFinder - collections;
//--- THINK !!!! - PagedFinder - simple paged (without counting all records) - this should work with only next/prev 
//PagedFinderWithCounter - paged with counting all records;    
//used for testing through the UI of the new finders:
//CollectionFinder, PagedFinder, PagedFinderWithCounter
 * 
 * @author dmakariev
 */
class DelegatingFinder implements Finder {

    private final Finder delegate;
    private final Object[] parameters;

    public DelegatingFinder(final MetaAction metaAction, final Object repository, final Object[] parameters) {
        this.parameters = parameters;
        try {
            final Method finderMethod = metaAction.getMethod();
            if (isPagedFinder(finderMethod)) {
                final Method counterMethod = extractCounterMethod(finderMethod);
                this.delegate = new PagedFinder(counterMethod, finderMethod, repository);
            } else {
                this.delegate = new CollectionFinder(finderMethod, repository);
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    public long counter(final Object... paramsNotUsed) {
        return delegate.counter(parameters);
    }

    @Override
    public List<Object> finder(final int firstResult, final int maxResult, final Object... paramsNotUsed) {
        return delegate.finder(firstResult, maxResult, parameters);
    }

    private static boolean isPagedFinder(final Method method) {
        final int indexFirstResult = ReflectionUtils.indexOf(method, DsFirstResult.class);
        final int indexMaxResult = ReflectionUtils.indexOf(method, DsMaxResult.class);
        final boolean isPagedFinder = (-1 != indexFirstResult) && (-1 != indexMaxResult);
        return isPagedFinder;
    }

    private static Method extractCounterMethod(final Method method) {
        /*
         * assuming this is a pagedFinder; (cannot efficiently 'page' the simple collection finders)
         * convention:
         *   if starts with findAAA, the counter can be:
         *       countFindAAA
         *       countAAA
         *   else if someName, the counter is :
         *       countSomeName
         */
        final Class[] probableParameters = probableParameters(method);
        final Class mainClass = method.getDeclaringClass();
        final String finderMethodName = method.getName();
        {
            final String counterMethodName = counterSecondPart(finderMethodName);
            final Method counterMethod = ReflectionUtils.getMethod(mainClass, counterMethodName, probableParameters);
            if (null != counterMethod) {
                return counterMethod;
            }
        }
        {
            final String counterMethodName = counterFull(finderMethodName);
            final Method counterMethod = ReflectionUtils.getMethod(mainClass, counterMethodName, probableParameters);
            if (null != counterMethod) {
                return counterMethod;
            }
        }
        return null;
    }

    private static Class[] probableParameters(final Method method) {
        final int indexFirstResult = ReflectionUtils.indexOf(method, DsFirstResult.class);
        final int indexMaxResult = ReflectionUtils.indexOf(method, DsMaxResult.class);
        final Class[] original = method.getParameterTypes();
        final Class[] probable = new Class[original.length - 2];
        int adjustIndex = 0;
        for (int i = 0; i < original.length; i++) {
            final boolean isPagingParameter = (i == indexFirstResult) || (i == indexMaxResult);
            if (false == isPagingParameter) {
                probable[i - adjustIndex] = original[i];
            } else {
                adjustIndex++;
            }
        }
        return probable;
    }

    private static String counterSecondPart(final String finderName) {
        if (finderName.startsWith("find") && finderName.length() > 4) {
            final String finderSecondPart = finderName.substring(4);
            return counterFull(finderSecondPart);
        } else {
            return "count";
        }
    }

    private static String counterFull(final String finderName) {
        if (finderName.length() > 1) {
            final String counterSecondPart = finderName.substring(0, 1).toUpperCase() + finderName.substring(1);
            return "count" + counterSecondPart;
        } else {
            final String counterSecondPart = finderName.toUpperCase();
            return "count" + counterSecondPart;
        }
    }
}
